package server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;

import scheduler.ILinkRepository;
import scheduler.IScheduler;
import structures.InfoPacket;
import structures.TaskType;
import crawlerGUI.CrawlerMainWindow;

class ShutDownManager {
	private static boolean isShuttingDown;

	public static boolean isShuttingDown() {
		return isShuttingDown;
	}

	public static void setShuttingDown() {
		ShutDownManager.isShuttingDown = true;
	}
}

// wątek do wysyłania nowych wątków
class ConnectionWriterThread implements Runnable {

	// socket konkretnego klienta
	private Socket sock;
	// scheduler dający kolejne zadania
	private IScheduler scheduler;
	// strumień do wysyłania do klientów
	private ObjectOutputStream out;
	// domyślny pakiet z zdaniami do wykonania
	private InfoPacket _defaultPacket;

	// zmienienie tasków poprzez przekazanie InfoPacket
	public void updateDefaultPacket(InfoPacket pck) {
		this._defaultPacket = pck;
	}

	// konstruktor
	ConnectionWriterThread(Socket sock, ILinkRepository linkRep,
			IScheduler scheduler, InfoPacket defaultPacket) {
		this.sock = sock;
		this._defaultPacket = defaultPacket;
		this.scheduler = scheduler;
	}

	public void run() {
		Thread shutDownThread = null;
		try {
			out = new ObjectOutputStream(sock.getOutputStream());
			String URL;
			while ((URL = scheduler.getTask()) != null) {

				InfoPacket packet = new InfoPacket(URL);
				packet._taskArguments = this._defaultPacket._taskArguments;

				if (ShutDownManager.isShuttingDown()) {
					System.out.println("Server kończy pracę ,powiadamiam klientów");
					packet._taskList = new LinkedList<TaskType>();
					packet._taskList.add(TaskType.shutdown);
					if(packet._taskList.contains(TaskType.shutdown))
						System.out.println("Po stronie serwera ok;");
					
					if (shutDownThread == null) {
						shutDownThread = new Thread(new Runnable() {

							@Override
							public void run() {
								try {
									Thread.sleep(200); // oczekuje, aż
														// wszsystkie Crawlery
														// się zakończą.
								} catch (InterruptedException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
								try {
									out.close();
								} catch (IOException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}

							}

						});
						shutDownThread.start();
					}

				} else
					packet._taskList = this._defaultPacket._taskList;
				out.writeObject(packet);
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

// ***********************************************************************************

class ConnectionReaderThread implements Runnable {

	private Socket sock;
	private ILinkRepository linkRep;
	private CrawlerMainWindow _window;
	private ObjectInputStream in;

	ConnectionReaderThread(Socket sock, ILinkRepository linkRep,
			IScheduler scheduler, CrawlerMainWindow window) {
		this.sock = sock;
		this.linkRep = linkRep;
		this._window = window;
	}

	public void run() {
		try {
			in = new ObjectInputStream(sock.getInputStream());
			InfoPacket pck = null;

			while ((pck = (InfoPacket) in.readObject()) != null) {
				/*
				System.out
						.println("CrawlerServer (ReaderThread) : odebrałem listę linków: "
								+ pck.getUrlList());
				*/
				linkRep.saveLinks(pck.getUrlList());
				this._window.updateURLs(pck.getUrlList());
				
				// przeszukuje listę zadań i w zależności od TaskType wykonuje
				// odpowiednią czynność
				for (TaskType t : pck.getTaskList()) {
					if (t == TaskType.keyword
							&& (Boolean) pck.getTaskArgument(TaskType.keyword)) {
						//System.out.println("Znalazłem ----------\n +");
						this._window.updatePhrasesFound(pck.getPage());
					} else if (t == TaskType.shutdownReturn) {
						System.out.println("Klient zakończył pracę, zakonczenie obsługi klienta.");
						in.close();
						sock.close();
						return;
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

}

// ***********************************************************************************

public class CrawlerServer implements Runnable {
	private ServerSocket srvSock;
	private ILinkRepository linkRep;
	private IScheduler scheduler;
	private CrawlerMainWindow _window;
	private InfoPacket _taskPacket;
	private ConnectionWriterThread _writer;
	private ConnectionReaderThread _reader;

	public CrawlerServer(int port, IScheduler scheduler,
			ILinkRepository linkRep, CrawlerMainWindow window,
			InfoPacket defaultPacket) {
		this.linkRep = linkRep;
		this.scheduler = scheduler;
		this._window = window;
		this._taskPacket = defaultPacket;
		try {
			srvSock = new ServerSocket(port);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void run() {
		System.out.println("CrawlerServer rozpoczyna działanie");
		Socket s;
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					srvSock.close();
				} catch (IOException e) {
					e.printStackTrace();
				}

			}
		}));

		try {

			while ((s = srvSock.accept()) != null) {
				this._window.addClient(s.getRemoteSocketAddress().toString());
				System.out.println("CrawlerServer zaakceptował połączenie");
				this._writer = new ConnectionWriterThread(s, linkRep,
						scheduler, this._taskPacket);
				new Thread(this._writer).start();
				this._reader = new ConnectionReaderThread(s, linkRep,
						scheduler, _window);
				new Thread(this._reader).start();
			}
		} catch (IOException e) {
		}
	}

	// do zmiany zadania
	public void updatePacket(InfoPacket pck) {
		this._writer.updateDefaultPacket(pck);
		this.scheduler.reset();

	}

	// do zatrzymywania schedulera - NIEZAIMPLEMENTOWANE !!!!!
	public synchronized void stop() {
		if (!ShutDownManager.isShuttingDown()) {
			ShutDownManager.setShuttingDown();
			System.out.println("Oznaczenie servera jako kończącego pracę. Oczekiwanie na powiadomienie klientów...");
			Thread t = new Thread(new Runnable() {

				@Override
				public void run() {
					try {
						Thread.sleep(2000); // oczekuje, aż wszsystkie Crawlery
											// się zakończą.
						srvSock.close();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					return;

				}

			});
			t.start();
			try {
				t.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("Zakończono pracę serwera");
			return;
		}
	}
}
